<?php

/*
 * This collection of functions (libary) is licensed under GPL2.
 * See LICENSE or www.gnu.org for more details.
 *
 * @author Henri Ranki & Jyry Kuukkanen 20.7.2001
 *
 * This file implements the output filter for html code. Dataxi uses
 * dzAsm class to produce the html code that it sends to the client.
 *
 * Below is a prototype for a class that implements an output filter
 * for Dataxi.
 *
 * class dzAsm {
 *     dzAsm(&$TemplatePtr, &$PurkkiPtr)
 *     getFormCode()
 * } //dzAsm
 *
 * The class must have two public methods the other is the constructor
 * Constructor takes two parameters. These are references to dzTemplate
 * and dzPurkki. Use these classes to collect all information for the form
 * and how to produce it.
 *
 * The other method getFormCode() should return the code that makes up the form
 * for view. The code is sent to client side.
 *
 * $Id: dzouthtml.php 595 2006-04-03 16:00:47Z jyry $
 */

include_once("sofiles.php");
include_once("sostring.php");
include_once("soset.php");
include_once("sodatetime.php");
include_once("sogeneric.php");

include_once("dzseppo.php");
include_once("dzgeneric.php");
include_once("dztemplate.php");
include_once("dzlaituri.php");
include_once("dzsrcentity.php");
include_once("dzfeaturespool.php");
include_once("dzstonga.php");
include_once("dzkeycodes.php");

/* The following is needed to get the html tag definitions */
include_once("dzinhtml.php");

/*
EXTERNAL FIELD NAMING CONVENTION

The field names on client side are names as presented below.
This appliens only with html output.

Basic name syntax is: n_g_y_x_name_e, where
    n   is either s, e or n,
        s single widget
        e expandable table widget. Not the new line
        n states that this widget is on the last
          row on a dataxi table (expandable rows).

    g   group id (table no.)
    y   similar destiny for y direction.
    x   x coordinate of the widget
    name Column name on the server side
    e   either c or i, stating whether this is calculated field or not
        c calculated field
        i normal field
*/


/** Output html filter
 *  Produces the html presentation of the form to send to client side.
 *  This class uses the information in dzTemplate and dzPurkki.
 */
class dzAsm extends soBaseClass {
    var $__IniCache;        /** dzIniItemCache Ini item cache to gain speed */
    var $__Template;        /** dzTemplate Pointer to dzTemplate used to get
                                the structure and the properties of the form. */
    var $__Laituri;         /** dzLaituri Pointer to the dzPurkki class that
                                contains the data for the form. */
    var $__Seppo;           /** dzScriptSeppo Script producer */
    var $__LastAccess;      /** str time of last access */
    var $__FirstField;      /** str name of the 1st field. (upper left corner.
                                 This is used to put the focus there */
    var $__TdAlign;         /** str How the curren td is aligned */
    var $__Mode;            /** DZM_* Current form mode (browse, edit...) */

    var $DZ_HEAD_SCRIPT_TAG = "VIRTAHEPOKAS"; /* temp flag in html */


    /** Constructor
     *  Initializes reference variables to dzTemplate and dzPurkki. DzOutHtml
     *  get the needed information to produce the html code from these classes.
     *  @param dzTemplate $TemplatePtr Reference to dzTemplate class
     *  @param string $LastAccess Last time this session accessed
     *  @param dzPurkki $LaituriPtr Reference to dzPurkki class
     */
    function dzAsm(&$TemplatePtr, $LastAccess, &$LaituriPtr) {
        $this->setup($TemplatePtr, $LastAccess, $LaituriPtr);
        $this->__Template->ClientWidgetMap = new soSet();
        $this->__Inis = new dzIniItemCache();

        $this->__Seppo = new dzScriptSeppo($TemplatePtr, $LaituriPtr);
        $this->Error = $this->__Seppo->Error;
    } // dzAsm



    /** For internal? calls only
     *  @param dzTemplate $TemplatePtr Reference to dzTemplate class
     *  @param string $LastAccess Last time this session accessed
     *  @param dzPurkki $LaituriPtr Reference to dzPurkki class
     */
    function setup(&$TemplatePtr, $LastAccess, &$LaituriPtr) {
        $this->__Template = &$TemplatePtr;
        $this->__Laituri = &$LaituriPtr;
        $this->__LastAccess = $LastAccess;
    } // setup


    /** Main function that returns code to be sent to the client
     *  Produces the HTML source to be send for the client. This includes the
     *  head script and form parts.
     *  @param soList/soErrorMsg $Messages List of error messages
     *  @param array/dzPageLinks $PageLinks Dataxi form page links
     *  @return string Produced html
     */
    function getFormCode($Messages, $PageLinks = array()) {
        /* get the type of the view */
        $type = $this->__Template->getViewData(DZ_VI_TYPE);
        $this->__Mode = $this->__Template->getViewData(DZ_VI_MODE);
        soDebug("dzOutHtml: getFormCode entered. mode=".
                $this->__Mode.", type=".$type, SOD_DET);

        /* Reset widget map */
        $this->__Template->ClientWidgetMap->clear();

        /* Print the error messages and the user notifies */
        $html = $this->printMessages($Messages);

        $feat_spool = &$this->__Seppo->ScriptFeatureSpool;
        switch ($type) {
            case DZ_VIEW_TYPE_PICK_UP:
                /* Set the buttons for pick up view. */
                if ($this->__Mode == DZM_BROWSE)
                    $btns = array(DZ_SYM_LEFT, DZ_SYM_RIGHT, DZ_SYM_OK,
                                  DZ_SYM_X, DZ_SYM_LIST_BROWSE);
                else
                    $btns = array(DZ_SYM_OK, DZ_SYM_X);
                $feat_spool->setAttrib(DZSA_BUTTON_LIST, $btns);
                /* no break belongs here */
            case DZ_VIEW_TYPE_PSEUDO:
            case DZ_VIEW_TYPE_DATAXI:
                /* Editing is allowed by default. Protected cols are checked
                   when creating the form. */
                $feat_spool->setAttrib(DZSA_ALLOW_EDIT,
                                                              1);

                /* Produce the html code for the form */
                $browse_mode =
                    $this->__Template->getViewData(DZ_VI_BROWSE_MODE);
                if (($this->__Mode == DZM_BROWSE) &&
                    ($browse_mode == DZBM_LIST)) {
                    /* Special case in browse mode where data rows are shown in
                       a list. */
                    $html .= $this->getListHtml();
                } else {
                    $this->__FirstField = '';
                    $html .= $this->getDataxiHtml(0);

                    /* Set the focus to the first field on the form
                    if the focus is not defined elsewhere. */
                    $spool = &$this->__Template->ScriptFeatureSpool;
                    $set_focus = $spool->isInitAction(DZIA_SET_FOCUS) ||
                                 $spool->isInitAction(DZIA_TO_NEXT_COMPONENT);
                    if (!$set_focus) {
                        $this->__Template->addInitAction(DZIA_SET_FOCUS,
                                                         $this->__FirstField);
                    } // if

                } // if $mode...
                break;

            case DZ_VIEW_TYPE_DIALOG:
                $html .= $this->getDialogHtml();
                break;
        } // switch ($type)

        /* Produce the head script that consists of JavaScript. */
        $hs = $this->__Seppo->getHeadScript();

        /* Add style sheets to head block */
        $site_inc = dzGetIniPath(DZID_SITE_INC_FOLDER);
        $links = !dzNoLinking();
        if ($links) {
            $hs .= "\n<link rel='stylesheet' href='".$site_inc.
                   "/".DZCSS_DEFAULT.".css'>\n".
                   "\n<link rel='stylesheet' href='".$site_inc.
                   "/calendar.css'>\n";
        } else {
            $hs .= dzGetStyleBlock(DZCSS_DEFAULT).
                   dzGetStyleBlock("calendar");
        }; // if

        if (in_array($this->__Mode, array(DZM_EDIT, DZM_NEW))) {
            if ($links) {
                $hs .= "\n<link rel='stylesheet' href='".$site_inc.
                       "/".DZCSS_EDIT.".css'>\n";
            } else {
                $hs .= dzGetStyleBlock(DZCSS_EXIT);
            }; // if
        };

        /** N.B. For some very stange and bizarre reason, the head script
         *  tag (place holder) gets repeated many time in the html source.
         *  The reason is truly a great mystery.
#         *  Hence the extra tags must be removed so that only one remains */
#        $html = soReplaceStr($html,
#            $this->DZ_HEAD_SCRIPT_TAG.$this->DZ_HEAD_SCRIPT_TAG,
#            $this->DZ_HEAD_SCRIPT_TAG);

        /* Add scripts and style sheet references to head block */
        $html = str_replace($this->DZ_HEAD_SCRIPT_TAG, $hs, $html);

        /* Reset the script feature spool. */
        $this->__Template->ScriptFeatureSpool = new dzScriptFeatureSpool();

        return $html;
    } // getFormCode


    /** Returns page top or bottom code.
     *  This wraps the actual Dataxi code inside a table where the top
     *  holds the links to pages on the form and bottom encloses the form.
     *  @param string $Type "top" or "bottom"
     *  @return string Html code for requested type.
     */
    function __getPageHtml($Type) {
        $pages = &$this->__Template->getPageLinks();
        $sub_pcount = 1;

        if ($Type == "top") {
            $page = &$this->__Template->getPage();
            $result = "<table border=1 cellpadding=0 cellspacing=0 align=".
                      $page->Align." name=pages><tr>\n";
            if ($sub_pcount) {
                $result .= "<td><table><tr>\n";
                foreach ($pages as $page) {
                    if ($this->__Template->PageNo == $page->Link) {
                        $result .=  "<td><h3>".
                                    $page->Title."</h3>";
                    } else {
                        $result .=  "<td class=pagelink>".
                                    "<A HREF=\"javascript:".
                                    "submitNalle('nalle', ".
                                    "'', ".
                                    "'EChangePage', ".
                                    "'".$page->Link."');\">".
                                    $page->Title."</A>";
                    };
                    $result .= "</td>\n";
                }; // foreach
                $result .= "</tr></table></td></tr>\n<tr><td>";
            }; // if
        } else {
            $result = "</td></tr></table>\n";
        }; // else if

        return $result;
    } // __getPageHtml


    /** Returns pre-code for an entity.
     *  Produces all the precode for a field. This includes all the
     *  sub-entities that the might be inside the entity this value is
     *      located.
     *  @param dzSourceEntity $Entity The entity which precode is produced
     *  @param int $Row The row number in the table
     *  @param int $InsideGroup "0"=not inside a group, "-1"=inside a group but
     *      on a heading row, ">0"=inside the group rows
     *  @return string pre code for the field
     */
    function printPrecode(&$Entity, $Row, $InsideGroup = 0) {

        global $_DZ_GLOBALS;
        $code_cache = &$_DZ_GLOBALS[DZGV_OBJECTS];
        if ($code_cache != "array") $code_cache = array();
        if ($Entity->Name) {
            $cache_key = "dzasm.".$Entity->Name;
        };
        if ($cache_key && (in_array($cache_key, $code_cache))) {
            $code = $code_cache[$cache_key];
        } else {

            /* Print opening tag "<type properties>" */
            $code = $Entity->PreCode.$Entity->Type;
            $props = $Entity->getProperties();
            if ($props) $code .= " ".$props;
            if ($Entity->Type == BODY_ENTITY_TAG) {
                $code .= " onLoad='initCode();'";
            }; // if ($Entity->Type == BODY_ENTITY_TAG)
            $code .= ">";

            /* Inside the body block is dz form */
            if ($Entity->Type == BODY_ENTITY_TAG) {
                $code .= $this->__getPageHtml("top").
                         $this->getFormHeader();
            }; // if ($Entity->Type == BODY_ENTITY_TAG)

            /* Any kids around? */
            $ccount = count($Entity->Children);

            if (($InsideGroup != 0) &&
                ($Entity->Type == TR_ENTITY_TAG) && ($ccount) &&
                (!in_array($this->__Mode, array(DZM_QUERY, DZM_BROWSE)))) {
                $code .= $this->__addInsDelButtons($InsideGroup, $Row);
            }; // if

            $code_cache[$cache_key] = $code;
        }; // else if

        /* Print all the kids */
        for ($c = 0; $c < $ccount; $c++) {
            if ($Entity->Children[$c]) {
                $code .= $this->getDataxiHtml($Entity->Children[$c],
                                              $Row, $InsideGroup);
            }; // if
        }; // for $c

        /* stuff just before actual value */
        if ($Entity->PreValue) {
            $code .= $Entity->PreValue;
        }; // if ($Entity->PreValue)

        return $code;
    } // printPrecode


    /** Returns code inside an entity
     *  Produce the code for the field. This is the code for the html widget,
     *  the value and the label just before the widget definition.
     *  @param dzSourceEntity $Entity The entity which precode is produced
     *  @param string $Coords Fields group and x and y coordinates. Used in
     *      fieldname. JavaScript uses these coordinates to implement moving
     *               on the form.
     *  @param int $Row The row number in the table
     *  @return string Code for the field
     */
    function printField(&$Entity, $Coords, $Row) {
        $code = "";
        if ($Entity->Name) {
            /* Is this a expand row, new row or single field */
            $group_id = $this->__Template->getGroupID($Entity->Name);
            if ($this->__Laituri->getGroupType($group_id) == DZGT_MAIN) {
                $field_type = "s";
            } else {
                if ($this->__Laituri->getNewRowNo($group_id) == $Row) {
                    $field_type = "n";
                } else {
                    $field_type = "e";
                }; // if
            }; // else if

            /* Find out the field type. It is whether c = calculated or
               i = normal (logical isn't it)  */
            $calculated = $this->__Template->getColProperty(
                $Entity->Name, DZCPT_CONTROL, DZCP_FORMULA);
            if ($calculated) {
                if (in_array($this->__Mode, array(DZM_BROWSE, DZM_EDIT))) {
                    $this->setCalcValue($Entity->Name,
                                        $Row,
                                        $calculated);
                }; // if Mode=browse
            }; // if calc
            $calculated = ($calculated) ? "c" : "i";

            /* Construct the field name on the form */
            $widget_name = $field_type."_".$group_id."_".$Coords."_".
                           $Entity->Name."_".$calculated;

            /* No periods allowed in the field names.*/
            $widget_name = soSwapStr($widget_name, ".", DZ_DR_STR);

            /* Preserve the name of the first field */
            if (!$this->__FirstField) $this->__FirstField = $widget_name;

            /* Check the protected col condition */

            /* 1. Get the value of the column */
            $value = $this->__Laituri->getStoreItem(TAFFEL, $Entity->Name, 0);

            /* 2. Check if the condition is true */
            $cond = $this->__Template->getColProperty(
                $Entity->Name, DZCPT_CONTROL, DZCP_LOCKFORM);

            /* 3. Check the condition. '*' is a wild card it matches anything
               but an empty value */
            if ($cond && ($cond == $value))
                $this->__Seppo->ScriptFeatureSpool->setAttrib(DZSA_ALLOW_EDIT,
                                                              false);
            else if (($cond == "**") ||
                     (($cond == "*") && (trim($value))))
                $this->__Seppo->ScriptFeatureSpool->setAttrib(DZSA_ALLOW_EDIT,
                                                              false);

            /* Store the widget name and link it to the entity. */
            $this->__Template->ClientWidgetMap->addItem($widget_name,
                               array($Entity->Name, $Row));

            /* Get the value of the data field */
            $field_value = $this->__Laituri->getStoreItem(TULLI,
                                                    $Entity->Name, $Row);

            /* Add label here in the front of the widget. */
            $code .= "<A NAME='L".$widget_name."'>";

            /* Get the widget code. In browse the components are not used, but
               the value is printed as is if defined so. */
            $use_widgets =
                $this->__Template->getFormOption(DZFO_USE_BROWSE_WIDGETS,
                                                 "bool");
            if (!$use_widgets) $use_widgets = 1;
            $key_col = $this->__Template->getColProperty(
                $Entity->Name, DZCPT_STRUCT, DZCP_KEY);
            $auto = $this->__Template->getColProperty(
                $Entity->Name, DZCPT_STRUCT, DZCP_DEFAULT);
            $auto = soExtractVal($auto, ";", 1);
            if ($key_col && $auto && ($this->__Mode == DZM_NEW)) {
                $lang = &$this->__Template->SessionInfo->Lang;
                $field_value = "[".dzGetSysMsg(__FILE__, "new", $lang)."]";
            } else {
                $key_col = 0;
            }; // if
            if ((($this->__Mode == DZM_BROWSE) && (!$use_widgets)) ||
                (($this->__Mode == DZM_NEW) && $key_col)) {
                $code .= $field_value;
            } else {
                $req = $this->__Template->getColProperty(
                    $Entity->Name, DZCPT_STRUCT, DZCP_REQUIRED);
                $code .= $this->__Seppo->getWidgetScript($Entity,
                                                         $widget_name,
                                                         $field_value,
                                                         $req,
                                                         $this->__TdAlign);
            } // if

        } else {
            /* no data from db table? -> print from html */
            if ($Entity->Value) $code .= $Entity->Value;
        }; // if ($Entity->Name)

        return $code;
    } // printField


    /** Calculates a value and srores it for specified col+row.
     *  This is used in browse mode when JS calculated values do not work.
     *  @param string $Column Target column
     *  @param int $RowNo Target row no.
     *  @param string $Formula Formula to calculate
     */
    function setCalcValue($Column, $RowNo, $Formula) {
        $Formula = str_replace(DZ_DR_STR, '.', $Formula);
        $sum = soParseVal($Formula, 'sum(', ')', 1, 1);
        if ($sum) {
            $cols = array();
            $i = 1;
            while ($name = soParseVal($Formula, '$', '$', $i++)) {
                $cols[] = str_replace(DZ_DR_STR, '.', $name);
            }; // while
            $start = 0;
            $end = $this->__Laituri->getRowCount(TULLI,
            implode(" ", $cols), 1);
            $Formula = $sum;
            $store_id = 'TULLI';
        } else {
            $start = $RowNo;
            $end = $RowNo +1;
            $store_id = 'TAFFEL';
        }; // else if

        $total = 0;
        for ($i = $start; $i < $end; $i++) {
            $f = $Formula;
            while ($name = soParseVal($f, '$', '$', 1)) {
                $name = str_replace(DZ_DR_STR, '.', $name);
                $value = doubleval($this->__Laituri->getStoreItem($store_id,
                                                                  $name,
                                                                  $i));
                $f = str_replace('$'.$name.'$', $value, $f);
            }; // while
            eval('$result = '.$f.';');
            $total += $result;
        }; // for

        /* As this procedure is run *after* outfiltering, the number
         * formatting must be done again but only for calculated fields */
        $numfmt = $this->__Template->getColProperty(
            $Column, DZCPT_STRUCT, DZCP_NUMFMT);
        if ($numfmt) {
            $total = soNumFmt($total,
                intval(soExtractVal($numfmt, ",", 1)),
                intval(soExtractVal($numfmt, ",", 2)));
        }; // if
        $this->__Laituri->setStoreItem(TULLI, $Column, $RowNo, $total);
    } // setCalcValue


    /** Returns post-code for an entity.
     *  Produce the post code of the given entity.
     *  @param dzSourceEntity $Entity The entity which precode is produced
     *  @return string The postcode of the field
     */
    function printPostcode(&$Entity) {
        $code = "";

        /* stuff right after actual value */
        if ($Entity->PostValue) $code .= $Entity->PostValue;

        /* Ending white plastic body tag? Under nation's flag? */
        if ($Entity->Type == BODY_ENTITY_TAG) {
            $code .= "</FORM>".$this->__getPageHtml("bottom");
        }; // if ($Entity->Type == BODY_ENTITY_TAG)

        /* closing tag */
        if ($Entity->PostCode)  $code .= $Entity->PostCode;

        return $code;
    } // printPostcode


    /** Adds code for ins/del buttons before the 1st col in a row.
     *  >$InsideGroup:int "0"=not inside a group, "-1"=inside a group but on a
     *      heading row, ">0"=inside the group rows
     *  >$Row:int   The row number in the group
     */
    function __addInsDelButtons($InsideGroup, $Row) {
        global $_DZ_GLOBALS;


        $show_del_btn =
            !in_array($this->__Laituri->getDeleteLimit($InsideGroup),
                array(DZGD_NONE));
        $show_ins_btn =
            !in_array($this->__Laituri->getInsertLimit($InsideGroup),
                array(DZGI_ONE_END, DZGI_ANY_END, DZGI_NONE));

        if (($this->__Laituri->getDelCount($InsideGroup)) &&
            ($this->__Laituri->getDeleteLimit($InsideGroup) == DZGD_ONE_ANY)) {
            $show_del_btn = false;
        }

        if (($this->__Laituri->getInsCount($InsideGroup)) &&
            ($this->__Laituri->getInsertLimit($InsideGroup) == DZGD_ONE_ANY)) {
            $show_ins_btn = false;
        }

        $result = "<TD width=1>";

        if ($InsideGroup > 0) {
            /* Construct a fake field name for the button. Used to locate
               the button. */
            $btn_name = "fake_".$InsideGroup."_".$Row."_x_x_x";

            /* Store the button to the widget map. So when returning we can
               locate the correct row. */
            $this->__Template->ClientWidgetMap->addItem($btn_name,
                               array($btn_name, $Row));

            /* Find out the key codes to emulate. First insert then delete */
            $key = $this->__getIniItem("keymap", "ins_line", "app");
            if (!$key) $key = "c-ins";
            $ins_key = $_DZ_GLOBALS[DZGV_KEYCODES][$key];
            /* Create the event handlers for the ins-button */
            $ins_event = "\"submitNalle('nalle', '".$btn_name."', ".
                         "'EKeyboard', '".$ins_key."');\"";

            $key = $this->__getIniItem("keymap", "del_line", "app");
            if (!$key) $key = "c-del";
            $del_key = $_DZ_GLOBALS[DZGV_KEYCODES][$key];
            /* Create the event handlers for the del-button */
            $del_event = "\"submitNalle('nalle', '".$btn_name."', ".
                         "'EKeyboard', '".$del_key."');\"";

            /* Show the del/ins button only if del/ins is allowed */
            $result .= "<NOBR>".($Row +1);
            if ($show_ins_btn) $result .= '<INPUT TYPE=BUTTON VALUE="+ "'.
                                          'ONCLICK='.$ins_event.'>';
            if ($show_del_btn) $result .= '<INPUT TYPE=BUTTON VALUE="- "'.
                                          'ONCLICK='.$del_event.'>';
            $result .= "</NOBR>";
        } // if $InsideGroup

        return $result."</TD>";
    } // __addInsDelButtons


    /** Returns form html code.
     *  Returns html code for Dataxi view. This is recursive function and you
     *  are supposed to give only the first two parameters.
     *  @param int $EntityNo Refers to the entitynro in dzStruct (not given by
     *      caller)
     *  @param int $RowNo The row number in a group (not given by caller)
     *  @param int $InsideGroup "0"=not inside a group, "-1"=inside a group
     *      but on a heading row, ">0"=inside the group rows
     *  @return string Cunstructed html
     */
    function getDataxiHtml($EntityNo = 0, $RowNo = 0, $InsideGroup = 0) {
        soDebug("dzOutHtml->getDataxiHtml(".$EntityNo.")",
                SOD_DET);
        $cache = &$this->__Template->getCache();
        $page_no = $this->__Template->PageNo;
        $page = &$this->__Template->getPage();

        static $y_coord = 0;
        static $x_coord = 0;
        $result = "";
        $entity = &$this->__Template->getEntityByNo($EntityNo);
        if ($entity == NULL) {
            soDebug("dzOutHtml: Illegal entity nro. ".
                    "(Maybe unserialize failed?)", SOD_HI_E);
            return NULL;
         } // if
        $icount = 1;
        $groups = &$cache->Groups;
        $group_ids = $groups->getKeys();

        /* Html table and table row requires some pre-arrangements. */
        switch ($entity->Type) {
            case TR_ENTITY_TAG:
                $group_id = dzGetGroupID($page_no, $EntityNo);
                $grp_id = soArrayFind($group_ids, $group_id, 1);
                if ($grp_id != -1) {
                    $icount = $this->__getMaxRowCount($entity);
                    $InsideGroup = $group_ids[$grp_id];
                } else {
                    if ($InsideGroup > 0) $InsideGroup = -1;
                }; // else if
                break;

            case HEAD_ENTITY_TAG:
                if (!strstr($this->DZ_HEAD_SCRIPT_TAG, $entity->PostValue)) {
                    $entity->PostValue .= $this->DZ_HEAD_SCRIPT_TAG;
                };
                break;

            case TBODY_ENTITY_TAG:
            case TABLE_ENTITY_TAG:
                $g = count($group_ids);
                $huu = $InsideGroup;
                while (--$g >= 0) {
                    $grp = $groups->getItem($group_ids[$g]);
                    if ($grp->ParentNo == $EntityNo) $InsideGroup = -1;
                }; // while

                if (dzGetIniItem(DZIS_LAYOUT, DZID_FORMAT, "app", "bool")) {
                    if (in_array($EntityNo, $page->InnerTables)) {
                        $spacing = intval(
                            dzGetIniItem(DZIS_LAYOUT, DZID_SPACING, "app"));
                        $padding = intval(
                            dzGetIniItem(DZIS_LAYOUT, DZID_PADDING, "app"));
                        $border = intval(
                            dzGetIniItem(DZIS_LAYOUT, DZID_BORDER, "app"));
                    } else {
                        $spacing = 0;
                        $padding = 0;
                        $border = 0;
                    }; // else if

                    $entity->setProperty("cellspacing", $spacing);
                    $entity->setProperty("cellpadding", $padding);
                    $entity->setProperty("border", $border);
                };

            case TD_ENTITY_TAG:
                $align = $entity->getProperty("align");
                $align = soRemoveQuotes($align);
                $align = soRemoveQuotes($align, "'");
                $this->__TdAlign = $align;
                break;
        }; // switch

        /* Loop as many times as necessary to produce all the rows. */
        for ($row = 0; $row < $icount; $row++) {
            if ($InsideGroup > 0) {
                if ($row == $this->__Laituri->getNewRowNo($InsideGroup)) {
                    $inside_group = -1;
                } else {
                    $inside_group = $InsideGroup;
                };
            } else {
                $inside_group = $InsideGroup;
            };

            /* The precode of the entity */
            $result .= $this->printPrecode($entity, $row, $inside_group);

            /* Actual value of entity (field) */
            $result .= $this->printField(
                $entity, $y_coord."_".$x_coord, $RowNo);
            /* The code after the actual value */
            $result .= $this->printPostcode($entity);

            switch ($entity->Type) {
                case TD_ENTITY_TAG:
                    if ($entity->Name) $x_coord++;
                    $this->__TdAlign = "";
                    break;

                case TR_ENTITY_TAG:
                    /* only advance rowno. if there were dz TD's,
                       i.e. skip non-dz rows */
                    if ($x_coord > 0) {
                        $y_coord++;
                        $x_coord = 0;
                    }
                    break;

                case TABLE_ENTITY_TAG:
                    $InsideGroup = 0;
                    break;

            }; // switch
        }; // for $row

        return $result;
    } // getDataxiHtml


    /** Get row count for specified group.
     *  This function returns the maximum number of rows in set of columns that
     *  belong to the same group (TR).
     *  >$ParentNo:int Entity no. of TR element (group)
     *  <:int Returns the max. row count in group
     */
    function __getMaxRowCount(&$ParentEntity) {
        $icount = count($ParentEntity->Children);
        $result = 0;
        for ($i = 0; $i < $icount; $i++) {
            $c_no = $ParentEntity->Children[$i];
            $entity = $this->__Template->getEntityByNo($c_no);
            $values = $this->__Laituri->getStoreCol(TULLI, $entity->Name);
            $rcount = count($values);
            if ($result < $rcount) $result = $rcount;
        }; // for $i

        return $result;
    } // __getMaxRowCount


    /**
     * Get the FORM tag together with general html variables.
     * <:str    The form header
     */
    function getFormHeader() {
        $session_id = $this->__Template->SessionInfo->Id;
        $user = $this->__Template->SessionInfo->UserID;
        $s = "<FORM NAME='NalleForm' METHOD=POST ACTION=".
             dzGetMainScriptName().">\n";
        /* Add general variables on the form */
        $s .= "<INPUT TYPE=HIDDEN NAME='".DZRI_USER."' VALUE='".$user."'>\n";
        $s .= "<INPUT TYPE=HIDDEN NAME='".DZRI_SESSION."' VALUE='".
              $session_id."'>\n";
        $s .= "<INPUT TYPE=HIDDEN NAME='".DZRI_LASTACCESS."' VALUE='".
              $this->__LastAccess."'>\n";
        $s .= "<INPUT TYPE=HIDDEN NAME='".DZRI_WIDGET."' VALUE=''>\n";
        $s .= "<INPUT TYPE=HIDDEN NAME='".DZRI_FRAME."' VALUE=''>\n";
        $s .= "<INPUT TYPE=HIDDEN NAME='".DZRI_EVENT."' VALUE=''>\n";
        $s .= "<INPUT TYPE=HIDDEN NAME='".DZRI_OPTIONS."' VALUE=''>\n";

        return $s;
    } // getFormHeader


    /** Produce the html code for a dialog view
     *  @return string The html code
     */
    function getDialogHtml() {
        $src_stream = $this->__Template->getViewData(DZ_VI_SOURCE);
        soDebug("dzOutHtml: Reading dialog from ".$src_stream, SOD_DET);

        /* We'll need head script to construct the stonga and the menu */
        $site_inc = dzGetIniPath(DZID_SITE_INC_FOLDER);
        $links = !dzNoLinking();

        $result = "<HTML><head>\n";
        if ($links) {
            $result .= "<link rel='stylesheet' href='".$site_inc.
                       "/".DZCSS_DEFAULT.".css'>\n".
                       $this->DZ_HEAD_SCRIPT_TAG;
        } else {
            $result .= "<style>".dzGetStyleBlock(DZCSS_DEFAULT)."</style>\n".
                       $this->DZ_HEAD_SCRIPT_TAG;
        };
        $result .= "\n</head>\n".
                   "<BODY onLoad='initCode();'>".
                   $this->getFormHeader();

        /* Try first from the applications directory then from
         * the dataxi's dialog directory */
        $path = dzGetIniPath(DZID_FORM_SOURCE_FOLDER);

        $found = 1;
        if (file_exists("$path/$src_stream")) {
            $stream = new soInputStream("file://$path/$src_stream");
            if ($stream->Error) $found = 0;
        } else {
            $found = 0;
        };
        if (!$found) {
            $path = dzGetDataxiPath()."/".DZIS_DIALOGS;
            $stream = new soInputStream("file://".$path."/".$src_stream);
            if ($stream->Error) {
                dzSysError("Could not find dialog file file://".
                            $path."/".$src_stream);
            } // if
        } // if

        /* Strip the ini part from the string */
        $tmp_arr = soSplitStr($stream->Data, "<!-- dataxi", 1, 0, 1);
        $html = $tmp_arr[0];
        $tmp_arr = soSplitStr($tmp_arr[1], "-->", 1, 0, 1);
        $html = $html.$tmp_arr[1];

        /* Get the translations */
        $transl = soParseVal($stream->Data, "<!-- dataxi", "-->", 1, 1);

        /* If translation section not defined insert html code as is. */
        if ($transl) {
            $result .= dzTranslateString($html, $transl,
                                         $this->__Template->SessionInfo->Lang);
        } else {
            $result .= $stream->Data;
        }; // else if

        $result .= "</FORM></BODY></HTML>";

        /* Next read the ini information for the dialog */
        $iniFile = new soIniData("file://".$path."/".$src_stream);

        /* Get the buttons to show in Stonga */
        $value = $iniFile->getValue("stonga", "buttons");
        $btn_arr = soExplode($value, " ");
        if ($iniFile->Error)
            $btn_arr[] = DZ_SYM_OK;
        $this->__Seppo->ScriptFeatureSpool->setAttrib(DZSA_BUTTON_LIST,
                                                      $btn_arr);

        return str_replace("\$site_inc", $site_inc, $result);
    } // getDialogHtml


    /** Produce the html code for a list view. This is a special case
     *  that applies only in browse mode.
     *  @param int $Current Current row no.
     *  @param str The html code
     */
    function getListHtml() {
        soDebug("dzOutHtml: Produce list view html", SOD_DET);

        $src_name = soParseFilename(
            $this->__Template->getViewData(DZ_VI_SOURCE));
        $lang = $this->__Template->SessionInfo->Lang;

        /* Get the browse list column information */
        $col_info = $this->__Template->getFormOption(DZFO_LIST_COLS);
        $cur_row = $this->__Laituri->getBrowsePos();

        /* We'll need head script to construct the stonga and the menu */
        $site_inc = dzGetIniPath(DZID_SITE_INC);
        $result = "<head>\n".
                   $this->DZ_HEAD_SCRIPT_TAG.
                   "<link rel=\"stylesheet\" href=\"".$site_inc.
                   "/".DZCSS_DEFAULT.".css\">\n".
                   "\n</head>\n".
                   "<HTML><BODY>".
                   $this->getFormHeader()."\n";

        /* Produce the table */
        $result .= "<TABLE BORDER=1 CELLSPACING=1><TR><TH>\n";

        /* Get the column names */
        $cols = $col_info->getKeys();

        /* Produce titles and get widht/aligns for each col */
        $widths = array();
        $aligns = array();
        $dbalias = $this->__Template->getDbAlias();
        if (!$dbalias) {
            $dbalias = dzGetIniItem(DZIS_GENERAL, DZID_DBALIAS, "app");
        };
        foreach ($cols as $col_name) {
            $info = $col_info->getItem($col_name);

            /* Translate the title for the column */
            list($table, $col) = soExplode($col_name, ".");
            $out_name = dzGetDBTranslation($dbalias, $table, $col, $lang);

            if (intval($info->Width) > 0) {
                $widths[$col_name] = " WIDTH=".$info->Width;
            }; // else if

            if ($info->Align) {
                $aligns[$col_name] = " ALIGN=".$info->Align;
            }; // else if

            $result .= "<TD>".$out_name."</TD>\n";
        } // foreach
        $result .= "</TH></TR>\n";

        $rcount = $this->__Laituri->getRowCount(TULLI, 0);
        for ($row_no = 0; $row_no < $rcount; $row_no++) {
            ($row_no == $cur_row) ? $current = "*" : $current = "";

            $result .= "<TR>\n".
                       "<TD><INPUT TYPE=BUTTON VALUE=' ".($row_no+1).$current.
                        " ' NAME=ListItem_".$row_no.
                        " OnClick='top.nalle.listButtonWrapper(event);'></TD>";
            foreach ($cols as $col_name) {
                $result .= "<TD".$widths[$col_name].$aligns[$col_name].">".
                           $this->__Laituri->getStoreItem(TULLI,
                                                          $col_name,
                                                          $row_no).
                           "</TD>\n";
            } // foreach
            $result .= "</TR>\n";
        } // for $row_no

        $result .= "</TABLE></FORM></HTML></BODY>\n";

        /* Add JavaScript, that is needed to implement the browse list */
        $this->__Seppo->ScriptFeatureSpool->addGenFeatures("browse_list");
        /* Only the change presentation mode button is shown in the stonga. */
        $this->__Seppo->ScriptFeatureSpool->setAttrib(DZSA_BUTTON_LIST,
                                                      array(DZ_SYM_X,
                                                            DZ_SYM_LIST_BROWSE));

        return $result;
    } // getListHtml


    /** Create notifes
     *  Produce the code for user notifications and error messages.
     *  @param soList/soErrorMsg $Messages List of error messages
     *  @return string The html code
     */
    function printMessages($Messages) {
        soDebug("dzOutHtml: Produce user messages", SOD_DET);
        if (soGetClass($Messages) != "dzerror") return ""; // <--- EXIT !!!

        /* Print each message */
        $html = "";
        $mcount = $Messages->getErrorCount();
        for ($c=0; $c < $mcount; $c++) {
            $html .= $this->printMessage($Messages->getError($c));
        } // for

        return $html;
    } // printMessages


    /** Create code for message
     *  Produce the code for one user notification. Function is considered
     *  @param soErrorMsg $Messages Message to print
     *  @return string The html code
     */
    function printMessage($Msg) {
        /* Set the parameters for each error type level */
        switch ($Msg->Level) {
            case DZERL_NOTIFY:
                $color = $this->__getIniItem(DZCL_SECTION, DZCL_ERROR_NOTIFY);
                break;

            case DZERL_WARNING:
                $color = $this->__getIniItem(DZCL_SECTION, DZCL_ERROR_WARNING);
                break;

            case DZERL_ERROR:
                $color = $this->__getIniItem(DZCL_SECTION, DZCL_ERROR_NORMAL);
                break;

            case DZERL_FATAL:
                $color = $this->__getIniItem(DZCL_SECTION, DZCL_ERROR_FATAL);
                break;

        } // switch

        /* Should we use an error base or do we have a string. */
        if (soSubStr($Msg->Text, 0, 1) == "#") {
            /* Using error database */
            $msg_id = soSubStr($Msg->Text, 1, strlen($Msg->Text));
            $msg_text = dzGetSysMsg(DZER_DATABASE, $msg_id,
                                $this->__Template->SessionInfo->Lang);
        } else {
            /* We have the message */
            $msg_text = $Msg->Text;
        } // soSubStr

        /* Replace tags with parameters given */
        $msg_text = $this->replaceMsgTags($msg_text, $Msg->Params);

        /* If the module is given print it too */
        if ($Msg->Module) {
            $msg_text = $Msg->Module.": ".$msg_text;
        } // $Msg->Module

        $html = "<CENTER><TABLE WIDTH=96% BORDER=1 bgcolor=$color><TR><TD>\n";
        $html .= "<CENTER>$msg_text</CENTER>\n";
        $html .= "</TD></TR></TABLE></CENTER>\n";

        return $html;
    } // printMessage


    /** Replaces the tags in the message string with given parameters
     *  @param string $Msg Message
     *  @param soset/string $Params   Parameters used to replace tags. soSet
     *       key is the tag name
     */
    function replaceMsgTags($Msg, $Params) {
        /* Check the parameters */
        if (soGetClass($Params) != "soset") $Params = new soSet();

        /* Replace all the tags. */
        while (($tag = soParseVal($Msg, "[", "]", 1))) {
            $Msg = soReplaceStr($Msg, "[".$tag."]", $Params->getItem($tag));
        } // while

        return $Msg;
    } // replaceMsgTags


    /** Gets an ini item.
     *  The only purpose for this wrapper method is to cache common ini
     *  items in order to speed up form building.
     */
    function __getIniItem($Section, $Key, $Ini = "app") {
        $result = $this->__Inis->dzGetValue($Ini, $Section, $Key);
        return $result;
    } // __getIniItem

} // class dzAsm
?>
